/**
 *  Copyright (C) 2008-2013  Telosys project org. ( http://www.telosys.org/ )
 *
 *  Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *          http://www.gnu.org/licenses/lgpl.html
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.telosys.common.output;

import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;

import org.telosys.common.dataset.DataSet;
import org.telosys.common.output.error.DataOutputError;
import org.telosys.common.output.error.OutputError;
import org.telosys.common.output.error.ViewOutputError;
import org.telosys.common.output.writer.DataSetXmlWriter;
import org.telosys.common.output.writer.ListXmlWriter;
import org.telosys.util.XmlUtil;
import org.telosys.util.beanwriter.BeanWriter;
import org.telosys.util.beanwriter.XmlBeanTranslator;

/**
 * Set of generic methods to generate the standard XML response fragments 
 * 
 * @author Laurent GUERIN
 * @since 1.0.5
 *
 */
public class XmlOutput 
{
	//--------------------------------------------------------------------------------------------------
    /**
     * Generates an element data tag for the given object instance <br>
     * The element can be : <br>
     * a Telosys collection ( VOList, DataSet ), <br>
     * a basic Java object ( String, Float, Boolean, Date, .. ), <br>
     * a specific application Value Object with a specific XML mapper
     * 
     * @param oDataElement
     * @param sElementName
     * @param out
     */
    public final static DataOutputError generateDataElement ( Object oDataElement, String sElementName, PrintWriter out )
    {
        DataOutputError error = null ;
        
	    if ( oDataElement != null )
	    {
	        try {
	    	    if ( oDataElement instanceof DataSet )
	    	    {
	    	        DataSetXmlWriter.toXml(out, (DataSet) oDataElement, sElementName );
	    	    }
	    	    else if ( oDataElement instanceof List ) // All "List" implementation, including BeanList
	    	    {
	    	    	List<?> list = (List<?>) oDataElement ;
	    			ListXmlWriter.toXml(out, list, sElementName, null); // The element class name will be used as tag name
	    	    }
	    	    else 
	    	    {
	    	    	//--- Basic BEAN object 
	    			XmlBeanTranslator xmlWriter = new XmlBeanTranslator(true, sElementName);
	    			//xmlWriter.setNullTag(null); // No Null Tag 
	    			//xmlWriter.setNullTag("NullObject"); // Null Tag 
	    			//xmlWriter.setWriteNullAttribute(false);
	    			BeanWriter bw = new BeanWriter(out, xmlWriter);
	    			
	    			bw.writeObject(oDataElement);
	    	    }
	        	
	
	        } catch (Throwable e)
	        {
	            error = new DataOutputError(oDataElement, sElementName, e);
	        }
//	        finally {
//	        }
	    }
	    else
	    {
    		out.println("<!-- Element " + sElementName + " is null -->" );
	    }
	    return error ;
    }
    
	//--------------------------------------------------------------------------------------------------
	/**
	 * Returns the cause of the given exception
	 * @param ex
	 * @return
	 */
	private static Throwable getCause ( Throwable ex )
	{
	    if ( ex != null )
	    {	        
//            if ( ex instanceof ServletException )
//            {
//                //--- ServletException : get "Root Cause"
//                ServletException ex2 = (ServletException) ex ;
//                return ex2.getRootCause();
//            }
//            else
//            {
//                //--- Other Exception : get "Standard Cause"
//                return ex.getCause();
//            }
            return ex.getCause();
	    }
	    return null ;
	}
	
	//--------------------------------------------------------------------------------------------------
	/**
	 * Generates the "output-errors" if necessary from the given DataOutputError and ViewOutputError
	 * @param dataError
	 * @param viewError
	 * @param out
	 */
	public static void generateOutputErrors ( DataOutputError dataError, ViewOutputError viewError, PrintWriter out)
	{
	    if ( ( null == dataError ) && ( null == viewError ) ) return ;
		out.println("<output-errors>" );
		try {
		    //--- Generate 0..1 <data-error> tags
		    if ( dataError != null )
		    {
		        //generateDataErrorTag ( dataError, out);		 
		        generateOutputErrorTag( dataError, out);	
			}
		    
		    //--- Generate 0..1 <view-error> tags
		    if ( viewError != null )
		    {
		        //generateViewErrorTag ( viewError, out);		        
		        generateOutputErrorTag( viewError, out);	
			}
		}
		finally {
			out.println("</output-errors>" );
		}
	}
	//--------------------------------------------------------------------------------------------------
	/**
	 * Generates the "output-errors" if necessary from the given collections of DataError and ViewError
	 * @param dataErrors 
	 * @param viewErrors
	 * @param out
	 */
	public static void generateOutputErrors ( LinkedList<DataOutputError> dataErrors, LinkedList<ViewOutputError> viewErrors, PrintWriter out)
	{
	    int n1 = dataErrors != null ? dataErrors.size() : 0 ;
	    int n2 = viewErrors != null ? viewErrors.size() : 0 ;
	    if ( n1 + n2 == 0 ) return ; // No error 
	    
		out.println("<output-errors>" );
		try {
		    //--- Generate 0..N <data-error> tags
		    generateOutputErrorTags ( dataErrors, out);
		    
		    //--- Generate 0..N <view-error> tags
		    generateOutputErrorTags ( viewErrors, out);
		}
		finally {
			out.println("</output-errors>" );
		}
	}
	//--------------------------------------------------------------------------------------------------
	/**
	 * Generates the <data-error> tags or <view-error> tags for a collection of OutputError objects
	 * @param errors
	 * @param out
	 */
	private static void generateOutputErrorTags ( LinkedList<? extends OutputError> errors, PrintWriter out)
	{
	    if ( null == errors ) return ; 
		int iCount = errors.size() ;
		if ( iCount > 0 )
		{
		    OutputError err = null ;
		    for ( int i = 0 ; i < iCount ; i++ )
		    {
		        err = errors.get(i);
		        generateOutputErrorTag ( err, out);		        
		    }
		}
	}
//	//--------------------------------------------------------------------------------------------------
//	private static void generateViewErrorTag ( ViewOutputError viewError, PrintWriter out)
//	{
//	    generateOutputErrorTag ( viewError, out);
//	}
//	//--------------------------------------------------------------------------------------------------
//	private static void generateDataErrorTag ( DataOutputError dataError, PrintWriter out)
//	{
//	    generateOutputErrorTag ( dataError, out);
//	}
	//--------------------------------------------------------------------------------------------------
	private static void generateOutputErrorTag ( OutputError outputError, PrintWriter out)
	{
        if ( outputError != null )
        {
            //--- Open the tag
            if ( outputError instanceof ViewOutputError )
            {
                ViewOutputError viewError = (ViewOutputError) outputError ;
    			out.print(" <view-error "
    			        + " view=\"" + viewError.getViewName() + "\""  
    			        + " renderer=\"" + viewError.getRenderer() + "\"" 
    			        );
            }
            else if ( outputError instanceof DataOutputError )
            {
                DataOutputError dataError = (DataOutputError) outputError ;
				out.print(" <data-error "
				        + " name=\"" + dataError.getElementName() + "\""  
				        + " class=\"" + dataError.getDataClassName() + "\"" 
				        );
            }
            else
            {
                return ;
            }
            
            Throwable ex = outputError.getException();
    		if ( ex != null )
    		{
    			out.print(" exception=\"" + ex.getClass().getName() + "\"" );
    		}
    		out.print(" message=\"" + XmlUtil.xmlString( outputError.getMessage() ) + "\"" );
			out.println(" >" );
    		
            //--- Generate tag content if any ( exception causes )
			if ( ex != null )
			{
				generateExceptionCauseTag ( ex, out);
			}

            //--- Close the tag
			if ( outputError instanceof ViewOutputError )
            {
				out.println("</view-error>");
            }
            else if ( outputError instanceof DataOutputError )
            {
    			out.println("</data-error>");
            }
        }
	}
	//--------------------------------------------------------------------------------------------------
	private static void generateExceptionCauseTag ( Throwable ex, PrintWriter out)
	{
	    if ( ex != null )
	    {
	        Throwable cause = getCause ( ex ) ;
	        while ( cause != null )
	        {
	            String sMessage = cause.getMessage() ;
	            if ( null == sMessage ) sMessage = "" ;
	            //--- Generate the cause tag
				out.println(" <cause "
				        + " exception=\"" + cause.getClass().getName() + "\""
				        + " message=\"" + sMessage + "\""
				        + " />" );		            
	            //--- Get the "cause of the cause"
	            cause = getCause ( cause ) ;
	        }
	    }
	}
	
    
}
